package com.apobates.jforum2.troll.replies.biz.impl;

import com.apobates.jforum2.troll.regular.ForumActionEnum;
import com.apobates.jforum2.troll.replies.biz.PostsMoodRecordsService;
import com.apobates.jforum2.troll.replies.biz.dao.PostsDao;
import com.apobates.jforum2.troll.replies.biz.dao.PostsMoodRecordsDao;
import com.apobates.jforum2.troll.replies.biz.impl.api.PostsActionLogStreamProvider;
import com.apobates.jforum2.troll.replies.entity.MoodCollectResult;
import com.apobates.jforum2.troll.replies.entity.Posts;
import com.apobates.jforum2.troll.replies.entity.PostsMoodRecords;
import com.apobates.jforum2.troll.utils.core.Commons;
import com.apobates.jforum2.troll.utils.core.api.ActionEventCulpritor;
import com.apobates.jforum2.troll.utils.persistence.Page;
import com.apobates.jforum2.troll.utils.persistence.Pageable;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.seata.spring.annotation.GlobalTransactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author xiaofanku
 * @since 20200527
 */
@Service
public class PostsMoodRecordsServiceImpl implements PostsMoodRecordsService{
    @Autowired
    private PostsMoodRecordsDao postsMoodRecordsDao;
    @Autowired
    private PostsDao postsDao;
    @Autowired
    private PostsActionLogStreamProvider postsActionLogStreamProvider;
    private final static Logger logger = LoggerFactory.getLogger(PostsMoodRecordsServiceImpl.class);
    
    @Override
    public Page<PostsMoodRecords> getAllByPosts(long postsId, Pageable pageable) {
        return postsMoodRecordsDao.findAllByPosts(postsId, pageable);
    }
    
    @Override
    public Stream<PostsMoodRecords> getByPosts(long postsId, boolean liked) {
        return postsMoodRecordsDao.findAllByMood(postsId, liked);
    }
    
    @Override
    public List<MoodCollectResult> getByTopic(final long topicId, final int page, final int pageSize) {
        CompletableFuture<Set<Long>> postsIdSet = CompletableFuture.supplyAsync(()->postsDao.findAllByTopic(topicId, page, pageSize)).thenApply(ps->ps.map(Posts::getId).collect(Collectors.toSet())).completeOnTimeout(Collections.emptySet(), 1, TimeUnit.SECONDS);
        final Map<Long, Map<Boolean, Long>> rs = postsIdSet.thenCompose(pids->CompletableFuture.supplyAsync(()->postsMoodRecordsDao.collectMembers(pids)).completeOnTimeout(Collections.emptyMap(), 1, TimeUnit.SECONDS)).orTimeout(1, TimeUnit.SECONDS).join();
        return foldMoodCollectResult(rs, topicId);
    }
    
    @Override
    public List<MoodCollectResult> getByPosts(long topicId, Set<Long> postsIdSet) {
        if (topicId == 0 || null == postsIdSet || postsIdSet.isEmpty()) {
            return Collections.emptyList();
        }
        Map<Long, Map<Boolean, Long>> rs = postsMoodRecordsDao.collectMembers(postsIdSet);
        return foldMoodCollectResult(rs, topicId);
    }

    @Override
    public Optional<MoodCollectResult> getByPostsId(long topicId,long postsId) {
        return postsMoodRecordsDao.findOneByPosts(topicId, postsId);
    }

    @Transactional
    @GlobalTransactional
    @Override
    public long toggleMoodRecord(long id, long topicId, boolean like, ActionEventCulpritor culpritor) {
        logger.error("[replies-api][SRV] toggle posts mood start");
        //不能重复操作(好人和坏人不能全作)
        Optional<Boolean> moodStats = postsMoodRecordsDao.removeMoodRecord(id, culpritor.getMemberId());
        if (moodStats.isPresent()) {
            //已经点过了都表示删除
            logger.error("[replies-api][SRV] remove mood action");
            sendMoodActionEvent(id, culpritor);
            return -1L;
        }
        logger.error("[replies-api][SRV] create mood action");
        PostsMoodRecords pmr = new PostsMoodRecords();
        pmr.setPostsId(id);
        pmr.setTopicId(topicId);
        pmr.setLiked(like);
        pmr.setMemberId(culpritor.getMemberId());
        pmr.setMemberNickname(culpritor.getMemberNickname());
        pmr.setEntryDateTime(LocalDateTime.now());

        try {
            logger.error("[replies-api][SRV] persiste mood action");
            postsMoodRecordsDao.save(pmr);
            if (pmr.getId() > 0) {
                logger.error("[replies-api][SRV] persiste mood success");
                //new PostsMoodEvent(pmr).sendBy(jmsTemplate);
                sendMoodActionEvent(id, culpritor);
                return pmr.getId();
            }
        } catch (Exception e) {
            logger.error("[replies-api][SRV] persiste mood fail, exception:"+e.getMessage());
            if(logger.isDebugEnabled()){
                logger.debug("回复喜好记录创建失败", e);
            }
        }
        return 0L;
    }
    //不满足通过注解来发送事件
    private void sendMoodActionEvent(long postsId, ActionEventCulpritor culpritor){
        logger.error("[replies-api][SRV] send mood event");
        //PostsMoodEvent
        //内部：
        //外部：{回复喜好动作通知}
        try {

            Posts posts = postsDao.findOneById(postsId);
            logger.error("[replies-api][SRV] query mood posts:"+posts.getId()+", arg postsId:"+postsId);
            postsActionLogStreamProvider.action(ForumActionEnum.POSTS_LIKED, posts, culpritor);
        }catch (Exception e){
            logger.error("[replies-api][SRV] send mood event fail, exception:"+e.getMessage());
            if(logger.isDebugEnabled()){
                logger.debug("喜好动作记录事件发送失败", e);
            }
        }
    }
    private List<MoodCollectResult> foldMoodCollectResult(final Map<Long, Map<Boolean, Long>> mrStats, final long topicId){
        BiFunction<Long, Map<Boolean, Long>, MoodCollectResult> bi = (postsId, moodStats)->{
            long likes = Commons.optional(moodStats.getOrDefault(true, 0L), 0L), hates = Commons.optional(moodStats.getOrDefault(false, 0L), 0L);
            return new MoodCollectResult(topicId, postsId, likes, hates);
        };
        return mrStats.keySet().parallelStream().map((postsId)->bi.apply(postsId, mrStats.get(postsId))).collect(Collectors.toList());
    }
}